import WatchBase from '@/utils/WatchValue';
import TranstionSelectList from '@/utils/TransitionSelectList';
import Visual from '../Visual';
import { BarInfo } from '../type';

const stateList = ["upRemove", "update", "downRemove"];

/** 数据条过渡类 */
export default class BarUtils {

	/** 可视化控制类 */
	private visual: Visual;

	/** 绘制数据条数据 */
	public barInfoList: BarInfo[];

	/** 绘制数据条数据 */
	public updateBarList: Set<BarInfo>;

	/** 数据条截取过渡列表 */
	public infoTransition: TranstionSelectList<BarInfo>;

	/** 数据条截取过渡列表 */
	public infoSelectTransition: TranstionSelectList<BarInfo>;

	/** 还需要绘制 */
	public get HasNext() {
		return Array.from(this.updateBarList).some(info => info.y.HasNext);
	}

	/**
	 * 构造函数
	 * @param visual 可视化控制类
	 */
	public constructor(visual: Visual) {
		this.visual = visual;
		this.barInfoList = [];
		this.infoTransition = new TranstionSelectList<BarInfo>(0, 0);
		this.infoSelectTransition = new TranstionSelectList<BarInfo>(0, 0);
		this.updateBarList = new Set();
	}

	/** 重置 */
	public Load() {
		const { visual, infoTransition, infoSelectTransition, updateBarList } = this;
		const { configUtils, dataUtils } = visual;
		const { firstIndx, maxLength } = configUtils.config;
		const { drawLoadData } = dataUtils;
		const { rowList } = drawLoadData;
		updateBarList.clear();
		infoTransition.Clear();
		infoTransition.Start = firstIndx.Value;
		infoTransition.Length = maxLength.Value;
		infoSelectTransition.Clear();
		infoSelectTransition.Start = firstIndx.Value;
		infoSelectTransition.Length = maxLength.Value;
		firstIndx.Add("remove", () => {
			infoTransition.Start = firstIndx.Value;
			infoSelectTransition.Start = firstIndx.Value;
		});
		maxLength.Add("remove", () => {
			infoTransition.Length = firstIndx.Value;
			infoSelectTransition.Length = firstIndx.Value;
		});
		this.barInfoList = rowList.map((_, index) => this.MakeBarInfo(index));
	}

	/** 创建数据条绘制项 */
	private MakeBarInfo(index: number): BarInfo {
		const { visual } = this;
		const { dataUtils, configUtils } = visual;
		const { drawLoadData } = dataUtils;
		const { rowList, iconList } = drawLoadData;

		const state = "downRemove";
		const info = rowList[index];
		const name = configUtils.makeFormatNameWatch(info);
		const color = new WatchBase<string>(configUtils.getInfoColor(info));
		const icon = new WatchBase<CanvasImageSource>(iconList[index] || new Image());
		const value = configUtils.makeValueTranstion(0);
		const width = configUtils.makeValueTranstion(0);
		const y = configUtils.makeYTranstion(0);
		const transparency = configUtils.makeYTranstion(0);
		const show = new WatchBase<boolean>(false);
		return { index, show, state, name, color, icon, value, width, y, transparency };
	}

	/** 更新数据 */
	public LoadingData() {
		const { visual, barInfoList, infoTransition, updateBarList } = this;
		const { dataUtils } = visual;
		const { drawReadData } = dataUtils;
		const { sortIndexList } = drawReadData;
		const showInfoList = sortIndexList.map(index => barInfoList[index]);
		infoTransition.Push(showInfoList);
		infoTransition.UpEnter.forEach(info => {
			info.state = "update";
			info.show.Value = true;
			this.CalcInfoLastWidth(info);
			updateBarList.add(info);
		});
		infoTransition.DownEnter.forEach(info => {
			info.state = "update";
			info.show.Value = true;
			this.CalcInfoLastWidth(info);
			updateBarList.add(info);
		});
		updateBarList.forEach(info => {
			this.CalcInfoWidth(info);
		});
	}

	private CalcInfoLastWidth(info: BarInfo) {
		const { visual } = this;
		const { configUtils, dataUtils } = visual;
		const { drawReadData, drawOldData } = dataUtils;
		if (drawOldData) {
			const { maxValue, minValue, valueList } = drawOldData;
			const value = valueList[info.index];
			const width = configUtils.CalcAxisWidth(minValue, maxValue, value);
			info.value.Value = value;
			info.width.Value = width;
		} else {
			const { saveData } = drawReadData;
			const { maxValue, minValue, valueList } = saveData;
			const value = valueList[info.index];
			const width = configUtils.CalcAxisWidth(minValue, maxValue, value);
			info.value.Value = value;
			info.width.Value = width;
		}
	}

	private CalcInfoWidth(info: BarInfo) {
		const { visual } = this;
		const { configUtils, dataUtils } = visual;
		const { drawReadData } = dataUtils;
		const { saveData } = drawReadData;
		const { maxValue, minValue, valueList } = saveData;
		const value = valueList[info.index];
		const width = configUtils.CalcAxisWidth(minValue, maxValue, value);
		info.value.Value = value;
		info.width.Value = width;
	}

	/** 绘制之前 */
	public BeforeDraw() {
		const { visual, infoSelectTransition, barInfoList, updateBarList } = this;
		const { configUtils, dataUtils, positionUtils } = visual;
		const { drawReadData } = dataUtils;
		const { YReverse, sortEveryFrame, ValueReverse } = configUtils.config;
		const sortEveryFrameV = sortEveryFrame.Value;
		if (!sortEveryFrameV && !positionUtils.OnLoadingData) {
			this.Trigger();
			return;
		}
		const YReverseV = YReverse.Value;
		const maxLength = infoSelectTransition.Length;
		const firstIndx = infoSelectTransition.Start;
		const valueUp = ValueReverse.Value ? -1 : 1;
		const valueList = barInfoList.map((info, index) => {
			const { width: { Target, Value } } = info;
			return { value: sortEveryFrameV ? Value : Target, index, info };
		});
		const infoSortList = valueList
			.sort((x, y) => y.value * valueUp - x.value * valueUp)
			.sort((x, y) => stateList.indexOf(x.info.state) - stateList.indexOf(y.info.state))
			.map(({ index }) => barInfoList[index]);
		infoSelectTransition.Push(infoSortList);
		infoSelectTransition.UpEnter.forEach((info) => {
			info.y.Value = YReverseV ? maxLength + 1 : -1;
			info.transparency.Value = 1;
			if (info.state !== "update") {
				info.state = "update";
				info.show.Value = true;
				this.CalcInfoLastWidth(info);
				updateBarList.add(info);
			}
		});
		infoSelectTransition.DownEnter.forEach((info) => {
			info.y.Value = !YReverseV ? maxLength + 1 : -1;
			info.transparency.Value = 1;
			if (info.state !== "update") {
				info.state = "update";
				info.show.Value = true;
				this.CalcInfoLastWidth(info);
				updateBarList.add(info);
			}
		});
		infoSelectTransition.Update.forEach((info) => {
			info.transparency.Value = 1;
		});
		infoSelectTransition.UpRemove.forEach(info => {
			info.state = "upRemove";
			info.y.Value = !YReverseV ? -1 : maxLength;
			info.transparency.Value = 0;
		});
		infoSelectTransition.DownRemove.forEach(info => {
			info.state = "downRemove";
			info.y.Value = !YReverseV ? maxLength : -1;
			info.transparency.Value = 0;
		});
		const updateList = infoSortList.slice(firstIndx, firstIndx + maxLength);
		updateList.forEach((info, i) => {
			info.y.Value = !YReverseV ? i : firstIndx + maxLength - 1 - i;
		})
		drawReadData.nowIcon.Value = updateList.length > 0 && updateList[0].icon.Value || new Image();
		this.Trigger();
	}

	/** 触发数据更新 */
	private Trigger() {
		const { updateBarList } = this;
		updateBarList.forEach(info => {
			info.value.Trigger();
			info.width.Trigger();
			info.y.Trigger();
			info.transparency.Trigger();
			const state = info.state;
			if (info.transparency.Value === 0 && (state === "upRemove" || state === "downRemove")) {
				info.show.Value = false;
				updateBarList.delete(info);
			}
		});
	}

}
